/*
 * scheduler.c
 *
 *  Created on: Mar 11, 2012
 *      Author: Christian
 */
#include "scheduler.h"


sched* scheduler_start(sched* sched_ptr, int num_processes, pcb** pcb_array, process** process_array, int sched_type) {
	//TODO - change the capacity back after testing.
	Queue aux_q = CreateQueue(num_processes+1);
	Queue key_q = CreateQueue(num_processes+2);
//	Queue sema_q = CreateQueue(num_processes+3);
	pcb** sema_q = (pcb**) malloc(sizeof(pcb*) * num_processes*100);
	pcb** ready_q = (pcb**) malloc(sizeof(pcb*) * num_processes);

	pcb* curr_pcb = (pcb*) malloc(sizeof(pcb*));
//	process* curr_process = (process*) malloc(sizeof(process*));

	sched_ptr->aux_q = aux_q;
	sched_ptr->key_q = key_q;
	sched_ptr->sema_q = sema_q;
	sched_ptr->ready_q = ready_q;
	sched_ptr->curr_pcb = curr_pcb;
//	sched_ptr->curr_process = curr_process;
	sched_ptr->sched_type = sched_type;
	sched_ptr->num_processes = num_processes;

	sched_ptr->sema_q_start = 0;
	sched_ptr->sema_q_end = 0;
	sched_ptr->sema_q_size = 0;
	sched_ptr->ready_q_start = 0;
	sched_ptr->ready_q_end = num_processes-1;

	scheduler_init_ready_q(sched_ptr, pcb_array, process_array);
	//figure out a way to point to first process to run, all other pcb states = READY

	return sched_ptr;
}

void scheduler_init_ready_q(sched* sched_ptr, pcb** pcb_array, process** process_array) {
	int sched_type = sched_ptr->sched_type;
	int num_processes = sched_ptr->num_processes;
	if (sched_type == ROUND_ROBBIN) { //RR
//		sched_ptr->curr_pcb = pcb_array[0];
//		sched_ptr->curr_pcb->state = RUNNING;
		for (int pid = 0; pid < num_processes; pid++) {
			sched_ptr->ready_q[pid] = pcb_array[pid];
		}
	} else if (sched_type == PRIORITY) { //PRI

	} else { //LOTTO

	}
}

/*
 * Handles switching running processes.
 */
int scheduler_switch_context(int interrupt, sched* sched_ptr, bool* ready_q_status) {
	//priority - every pcb has a priority number, highest priority moves to front of queue
	//...to prevent hi-pri processes from running indefinitely, every clock tick, curr pri decreases
	//...when timer is up, comparison made between this and next to decide which goes next
	//lottery - randomly choose a pid
	//printf("\t\t**Readyqueue status is: %i", *ready_q_status);
	int sched_type = sched_ptr->sched_type;
	int q_end_ptr = sched_ptr->ready_q_end;
	int q_start_ptr = sched_ptr->ready_q_start;
	if (sched_type == 0) { //RR - keep track of which are ready and move to back of queue
		if (interrupt == -1) {
			sched_ptr->curr_pcb = sched_ptr->ready_q[0];
			sched_ptr->curr_pcb->state = RUNNING;
			sched_ptr->ready_q_start++;
		} else if (*ready_q_status == true) { //if there is a process waiting in the ready_q
			if (interrupt == KEY) {
				printf("\t\t***Interrupted by KEYBOARD***\n");
				sched_ptr->curr_pcb->state = BLOCKED;
				sched_ptr->curr_pcb->waiting_on = NONE;
				//place this in key_q
				Enqueue((pcb*) sched_ptr->curr_pcb, sched_ptr->key_q);
				*key_flag = 1;
			} else if (interrupt == AUX) {
				printf("\t\t***Interrupted by AUXILIARY***\n");
				sched_ptr->curr_pcb->state = BLOCKED;
				//place this in aux_q
				Enqueue((pcb*) sched_ptr->curr_pcb, sched_ptr->aux_q);
				*aux_flag = 1; //TODO - Changed to signal aux to "process".
			} else if (interrupt == TIMER) {
				printf("\t\t***I'm interrupted by TIMER***\n");
				sched_ptr->curr_pcb->state = RUNNABLE;
				//place this in ready_q
				q_end_ptr = (sched_ptr->ready_q_end + 1) % sched_ptr->num_processes;
				sched_ptr->ready_q[q_end_ptr] = sched_ptr->curr_pcb;
				sched_ptr->ready_q_end = q_end_ptr;
				*ready_q_status = true;
			} else if (interrupt == SEMA) {
				printf("\t\t***Interrupted by SEMAPHORE***\n");
				sched_ptr->curr_pcb->state = BLOCKED;
				sched_ptr->curr_pcb->waiting_on = SEMA;
				//place this in sema_q
				//TODO: check if queue is full before enqueue
				int s_q_end_ptr = sched_ptr->sema_q_end;
				if (sched_ptr->sema_q_size < sched_ptr->num_processes) {
					s_q_end_ptr = sched_ptr->sema_q_end;
					sched_ptr->sema_q[s_q_end_ptr] = sched_ptr->curr_pcb;
					s_q_end_ptr = (s_q_end_ptr + 1) % sched_ptr->num_processes; //end is pointing to one after end pcb
					sched_ptr->sema_q_end = s_q_end_ptr;
					sched_ptr->sema_q_size++;
					printf("sema_q_size: %i", sched_ptr->sema_q_size);
				}
			}
			//get first element from ready_q and set it to current
			if (q_end_ptr != q_start_ptr) {
				q_start_ptr = sched_ptr->ready_q_start;
				sched_ptr->curr_pcb = sched_ptr->ready_q[q_start_ptr];
				sched_ptr->ready_q_start = (q_start_ptr + 1) % sched_ptr->num_processes;
				sched_ptr->curr_pcb->state = RUNNING;
			} else {
				printf("\nREADY QUEUE IS EMPTY!!!!!\n");
				*ready_q_status = false;
				printf("ready q in scheduler context switch: %i\n", *ready_q_status);
			}
		}
	} else if (sched_type == 1) { //PRI

	} else { //LOTTO

	}
	return sched_ptr->curr_pcb->pid;
}

/*
 * Handles switching BLOCKED processes to RUNNABLE.
 * Wakes sleeping semaphores and blocked processes waiting for keyboard and aux.
 */
void handle_interrupt(int interrupt, sched* sched_ptr, bool* ready_q_status, int semaphore) {
	//don't forget to set waiting_on to NONE
	//set state from BLOCKED to RUNNABLE
	//stick in ready_q
	printf("\t\t***my interrupt type is: %i", interrupt);
	if (interrupt == AUX) {
		//take head of aux_queue
		printf("***I'm in the handle_interrupt Aux option.***\n");
		pcb* the_pcb = (pcb*)FrontAndDequeue(sched_ptr->aux_q);
		//change state of pcb
		the_pcb->state = RUNNABLE;
		the_pcb->waiting_on = NONE;
		//add to ready queue
		int q_end_ptr = (sched_ptr->ready_q_end + 1) % sched_ptr->num_processes;
		sched_ptr->ready_q[q_end_ptr] = the_pcb;
		sched_ptr->ready_q_end = q_end_ptr;

		//if aux_queue has another process waiting, change aux_flag
		if(!IsEmpty(sched_ptr->aux_q)){
			*aux_flag = 1; //tell auxiliary to process.
		}else {
			*aux_flag = 2; //stalling aux from sending value.
		}
	} else if (interrupt == KEY) {

	} else if (interrupt == SEMA) { //when the program starts, the pair will try to wake each other up when not needed
		pcb* the_pcb;
		int start = sched_ptr->sema_q_start;
		int end = sched_ptr->sema_q_end;
		int size = sched_ptr->sema_q_size;
		int capacity = sched_ptr->num_processes;
		int counter = start;
		int found = -1;
		for (int i = 0; i < size; i++) {
			int owns = sched_ptr->sema_q[counter]->owns;
			if (owns == semaphore) {
				//grab the semaphore in the sema_q
				found = counter;
				the_pcb = sched_ptr->sema_q[found];
			}
			counter = (counter + 1) % capacity;
		}
		if (found != -1) { //first pass wakes will be to nobody
			//change the pcb's state
			the_pcb->state = RUNNABLE;
			the_pcb->waiting_on = NONE;
			//delete pcb from sema_q
			//WTF IS WRONG WITH THESE FOLLOWING LINES?!?!?
//			sched_ptr->sema_q[found] = sched_ptr->sema_q[end]; //STATUS_ACCESS_VIOLATION
//			end = (end - 1) % capacity;
//			sched_ptr->sema_q_end = end;
			//add to ready queue
			int q_end_ptr = (sched_ptr->ready_q_end + 1) % sched_ptr->num_processes;
			sched_ptr->ready_q[q_end_ptr] = the_pcb;
			sched_ptr->ready_q_end = q_end_ptr;
		}
	}
	*ready_q_status = true;
}
